package com.umessage.hotel.service.impl;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.PageHelper;
import com.riversoft.weixin.pay.base.PaySetting;
import com.riversoft.weixin.pay.payment.Payments;
import com.riversoft.weixin.pay.payment.bean.RefundRequest;
import com.riversoft.weixin.pay.payment.bean.RefundResponse;
import com.riversoft.weixin.pay.payment.bean.UnifiedOrderRequest;
import com.riversoft.weixin.pay.payment.bean.UnifiedOrderResponse;
import com.riversoft.weixin.pay.util.SignatureUtil;
import com.umessage.common.domain.ResponseBo;
import com.umessage.common.service.impl.BaseService;
import com.umessage.common.util.Atools;
import com.umessage.common.util.HttpRequestUtil;
import com.umessage.common.util.SimpleSmsUtil;
import com.umessage.hotel.dao.OrderInfoMapper;
import com.umessage.hotel.domain.HotelInfo;
import com.umessage.hotel.domain.MemberAccountLog;
import com.umessage.hotel.domain.MemberInfo;
import com.umessage.hotel.domain.OrderInfo;
import com.umessage.hotel.domain.QueryVo2OrderInfo;
import com.umessage.hotel.domain.WxCfg;
import com.umessage.hotel.model.PaySignModel;
import com.umessage.hotel.service.HotelInfoService;
import com.umessage.hotel.service.MemberAccountLogService;
import com.umessage.hotel.service.MemberCouponService;
import com.umessage.hotel.service.MemberInfoService;
import com.umessage.hotel.service.OrderInfoService;
import com.umessage.hotel.service.OrderPmsService;
import com.umessage.hotel.service.RoomInfoService;
import com.umessage.hotel.service.WxCfgService;
import com.umessage.hotel.util.HotelConstant;

import net.sf.json.JSONObject;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

@Service("orderInfoService")
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public class OrderInfoServiceImpl extends BaseService<OrderInfo> implements OrderInfoService {

	private Logger logger = LoggerFactory.getLogger(OrderInfoServiceImpl.class);

	private DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
	private DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	@Autowired
	private OrderInfoMapper orderInfoMapper;
	@Autowired
	private WxCfgService wxCfgService;
	@Autowired
	private MemberInfoService memberInfoService;
	@Autowired
	private MemberCouponService memberCouponService;
	@Autowired
	private RoomInfoService roomInfoService;
	@Autowired
	private MemberAccountLogService memberAccountLogService;
	@Autowired
	private HotelInfoService hotelInfoService;
	@Autowired
	private SimpleSmsUtil simpleSmsUtil;
	@Autowired
	private OrderPmsService orderPmsService;
	@Value("${hotel.payUrl}")
	private String payUrl;
	
	@Value("${umessage.miniprogramServerUrl}")
	private String miniprogramServerUrl;
	

	private boolean isSynchro(String hotelId){
		HotelInfo hotelinfo = hotelInfoService.findHotelsById(hotelId);
		if( null  ==  hotelinfo) return false;
		return "true".equals( hotelinfo.getOpenPms() ) ? true :false ;
	}
	
	@Override
	public OrderInfo findSingleOrder(String memberId, String orderId) {
		return orderInfoMapper.findSingleOrder(memberId, orderId);
	}

	@Override
	public List<OrderInfo> findUserOrderList(String memberId, String orderStatus, int page, int rows) {
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setMemberId(memberId);
		orderInfo.setOrderStatus(orderStatus);
		return this.findOrderList(orderInfo, page, rows);
	}

	@Override
	public List<OrderInfo> findHotelOrderList(String hotelId, String orderStatus, int page, int rows) {
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setHotelId(Integer.valueOf(hotelId));
		orderInfo.setOrderStatus(orderStatus);
		return this.findOrderList(orderInfo, page, rows);
	}

	@Override
	public void confirmOrder(String hotelId, String accountId, String orderId) {
		OrderInfo order = selectByKey(orderId);
		if(HotelConstant.ORDER_STATUS_CONFIRMED.equals(order.getOrderStatus())) {
			throw new RuntimeException("订单已确认");
		}
		if(HotelConstant.ORDER_STATUS_CANCELED.equals(order.getOrderStatus())){
			throw new RuntimeException("订单已经取消,不可再次确认");
		}
		if(HotelConstant.ORDER_PAY_STATE.equals(order.getPayStatus())){
			throw new RuntimeException("订单未支付,请进行支付");
		}
		if(this.isSynchro(hotelId)){//订单同步pms
			boolean flage = orderPmsService.establishPmsOrder(order);
			if(!flage){
				throw new RuntimeException("1044");
			}
		}
		
		order.setOrderStatus(HotelConstant.ORDER_STATUS_CONFIRMED);
		order.setConfirmPerson(accountId);
		order.setConfirmTime(new Date());
		updateAll(order);
	}

	/**
	 * 酒店取消订单
	 */
	@Override
	public void cancelOrderByHotel(String wxcfgid, String hotelId, String accountId, String orderId, String reason) {
		cancelOrderByHotel(wxcfgid, HotelConstant.OPERATOR_TYPE_HOTEL, accountId, orderId, true, reason);
	}

	/**
	 * 用户取消订单
	 */
	@Override
	public void cancelOrderByMember(String wxcfgid, String hotelId, String memberId, String orderId, String reason) {
		cancelOrder(wxcfgid, HotelConstant.OPERATOR_TYPE_MEMBER, memberId, orderId, true, reason);
	}

	/**
	 * 客户取消订单
	 * 
	 * @param operatorType 操作员类型
	 * @param operator     操作员id
	 * @param orderId      订单id
	 * @param byPolicies   是否依据政策
	 */
	@Transactional(rollbackFor = Exception.class)  
	@Override
	public void cancelOrder(String wxcfgid, Integer operatorType, String operator, String orderId, boolean byPolicies,
			String reason) {
		try {
			OrderInfo order = selectByKey(orderId);
			// 订单不存
			if (order == null) {
				throw new RuntimeException("不合法的订单id "+orderId);
			}
			
			//已入住不可取消
			if("3".equals( order.getOrderStatus() )){
				throw new RuntimeException("已入住订单不可取消 "+orderId);
			}
			
			// 订单已经取消 直接返回
			if (order.getOrderStatus().equals(HotelConstant.ORDER_STATUS_CANCELED)) {
				return;
			}
			
			//已确认  + 当天入住  18:00后不可取消
			if("1".equals(order.getOrderStatus())){
				int orderCheckin = Integer.parseInt(order.getCheckinTime().split(" ")[0].replaceAll("-", ""));
				String time = df.format(new Date());
				int nowTime  = Integer.parseInt(time.replaceAll("-", ""));
				if(orderCheckin == nowTime){
					int hour =Integer.parseInt( time.split("-")[1] );
					if(hour>=18){
						throw new RuntimeException("今日入住订单18:00后不可取消 "+orderId);
					}
				}
			}
			
			if(this.isSynchro(order.getHotelId()+"")){//取消pms订单
				orderPmsService.cancelbookPmsOrder(order);
			}
			
			//未支付直接取消
			if(order.getPayStatus()  == 0 ){
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			
			//判断房间政策不可退款 直接取消
			if("不可退款".equals(order.getNotCancle())) {
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			
			LocalDate checkInDate = LocalDate.parse(order.getCheckinTime().substring(0, 19), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
			LocalDate checkOutDate = LocalDate.parse(order.getCheckoutTime().substring(0, 19), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
			
			//到店支付  直接取消
			if(order.getPayStatus()  == 2){
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			// 支付类型为在线支付+已支付 
			if (HotelConstant.ORDER_PAY_TYPE_ONLINE.equals(order.getPayType()) && order.getPayStatus() == 1) {
				// 不按政策或者不按政策直接退款 		
				if (!byPolicies || byPolicies ) {
					int refundFee = order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue(); 
					int totalFee = refundFee;
					
					//查询消费记录
					List<MemberAccountLog> AccountList = memberInfoService.getConsumingRecordByUser(order.getHotelId()+"",order.getMemberId(),orderId,"1");
					//退金额
					if(null != AccountList && AccountList.size()>0){
						for (MemberAccountLog account : AccountList) { 
							if( "1".equals(account.getPayMode()) && !"3".equals(account.getType())){ //订单消费 +非微信支付
								//查询账户信息
								MemberInfo memberInfo = memberInfoService.getMemberInfoForUpdate(order.getMemberId());
								memberInfo.setRealMoney(memberInfo.getRealMoney().add(account.getRealMoney()));//退回虚拟和原账户
								memberInfo.setVirtMoney(memberInfo.getVirtMoney().add(account.getVirtMoney()));
								memberInfoService.updateAll(memberInfo);// 更新用户余额
								
								account.setPayStatus("2");
								memberAccountLogService.updateNotNull(account); //记录退款
							}
						}
					}
					if(order.getTotalPrice().compareTo(BigDecimal.ZERO)>0){
						refundRequest(wxcfgid, order.getOrderId(), refundFee, totalFee);//微信退款
					}
					saveOrderCancelStatus(order, operator, operatorType, reason); // 更新订单状态为：已取消
				}else {
					throw new RuntimeException("不符合取消条件的订单");
				}
			} 
			//担保支付   退微信
			if(order.getPayType() == 2  && order.getPayStatus() == 1){
				if(checkOutDate.isBefore(LocalDate.now())){ // 入住前一天可取消
					int refundFee = order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue(); 
					int totalFee = refundFee;
					if(order.getTotalPrice().compareTo(BigDecimal.ZERO)>0){
						refundRequest(wxcfgid, order.getOrderId(), refundFee, totalFee);// 微信退款
					}
					saveOrderCancelStatus(order, operator, operatorType, reason);// 更新订单状态为：已取消
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(" 取消订单失败："+ e.getMessage());
		}
	}
	/**
	 * 酒店取消订单
	 * 
	 * @param operatorType 操作员类型
	 * @param operator     操作员id
	 * @param orderId      订单id
	 * @param byPolicies   是否依据政策
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override  
	public void cancelOrderByHotel(String wxcfgid, Integer operatorType, String operator, String orderId, boolean byPolicies,
			String reason) {
		try {
			OrderInfo order = selectByKey(orderId);
			// 订单不存在
			if (order == null) {
				throw new RuntimeException("不合法订单 ");
			}
			
			//已入住不可取消
			if("3".equals( order.getOrderStatus() )){
				throw new RuntimeException("已入住订单不可取消 ");
			}
			
			if(this.isSynchro(order.getHotelId()+"")){
				//取消pms订单
				orderPmsService.cancelbookPmsOrder(order);
			}
			
			//判断房间政策 不可退款,直接取消订单
			if( "不可退款".equals(order.getNotCancle())) {
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			
			// 订单已经取消直接返回
			if (order.getOrderStatus().equals(HotelConstant.ORDER_STATUS_CANCELED)) {
				return;
			}
			
			//未支付直接取消
			if(order.getPayStatus()  == 0 ){
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			//到店支付,直接取消
			if(order.getPayType() == 1){
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			
			// 在线支付+已支付
			if (HotelConstant.ORDER_PAY_TYPE_ONLINE.equals(order.getPayType()) && order.getPayStatus() == 1) {
					int refundFee = order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue(); 
					int totalFee = refundFee;
					
					//查询消费记录
					List<MemberAccountLog> AccountList = memberInfoService.getConsumingRecordByUser(order.getHotelId()+"",order.getMemberId(),orderId,"1");
					//退金额
					if(null != AccountList && AccountList.size()>0){
						for (MemberAccountLog account : AccountList) { 
							if( "1".equals(account.getPayMode()) && !"3".equals(account.getType())){ //订单消费 +非微信支付
								//查询账户信息
								MemberInfo memberInfo = memberInfoService.getMemberInfoForUpdate(order.getMemberId());
								memberInfo.setRealMoney(memberInfo.getRealMoney().add(account.getRealMoney()));//退回虚拟和原账户
								memberInfo.setVirtMoney(memberInfo.getVirtMoney().add(account.getVirtMoney()));
								memberInfoService.updateAll(memberInfo);// 更新用户余额
								
								account.setPayStatus("2");
								memberAccountLogService.updateNotNull(account); //记录退款
							}
						}
					}
					if(order.getTotalPrice().compareTo(BigDecimal.ZERO)>0){
						refundRequest(wxcfgid, order.getOrderId(), refundFee, totalFee);//微信退款
					}
					saveOrderCancelStatus(order, operator, operatorType, reason); // 更新订单状态为：已取消
			} 

			//担保支付,退微信
			if(order.getPayType() == 2  && order.getPayStatus() == 1){
				int refundFee = order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue(); 
				int totalFee = refundFee;
				if(order.getTotalPrice().compareTo(BigDecimal.ZERO)>0){
					refundRequest(wxcfgid, order.getOrderId(), refundFee, totalFee);// 退款
				}
				saveOrderCancelStatus(order, operator, operatorType, reason);// 更新订单状态为：已取消
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("取消订单失败：" + e.getMessage());
		}
	}
	/**
	 * 酒店取消订单
	 * 
	 * @param operatorType 操作员类型
	 * @param operator     操作员id
	 * @param orderId      订单id
	 * @param byPolicies   是否依据政策
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override  
	public void cancelOrderByAuto(String wxcfgid, Integer operatorType, String operator, OrderInfo order, boolean byPolicies,
			String reason) {
		try {
			
			//已入住不可取消
			if("3".equals( order.getOrderStatus() )){
				throw new RuntimeException("已入住订单不可取消 "+order.getOrderId());
			}
			
			// 订单已经取消直接返回
			if (order.getOrderStatus().equals(HotelConstant.ORDER_STATUS_CANCELED)) {
				return;
			}
			
			//未支付直接取消
			if(order.getPayStatus()  == 0 ){
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			//到店支付,直接取消
			if(order.getPayType() == 1){
				saveOrderCancelStatus(order, operator, operatorType, reason);
				return;
			}
			
			// 在线支付+已支付
			if (HotelConstant.ORDER_PAY_TYPE_ONLINE.equals(order.getPayType()) && order.getPayStatus() == 1) {
				int refundFee = order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue(); 
				int totalFee = refundFee;
				
				//查询消费记录
				List<MemberAccountLog> AccountList = memberInfoService.getConsumingRecordByUser(order.getHotelId()+"",order.getMemberId(),order.getOrderId(),"1");
				//退金额
				if(null != AccountList && AccountList.size()>0){
					for (MemberAccountLog account : AccountList) { 
						if( "1".equals(account.getPayMode()) && !"3".equals(account.getType())){ //订单消费 +非微信支付
							//查询账户信息
							MemberInfo memberInfo = memberInfoService.getMemberInfoForUpdate(order.getMemberId());
							memberInfo.setRealMoney(memberInfo.getRealMoney().add(account.getRealMoney()));//退回虚拟和原账户
							memberInfo.setVirtMoney(memberInfo.getVirtMoney().add(account.getVirtMoney()));
							memberInfoService.updateAll(memberInfo);// 更新用户余额
							
							account.setPayStatus("2");
							memberAccountLogService.updateNotNull(account); //记录退款
						}
					}
				}
				if(order.getTotalPrice().compareTo(BigDecimal.ZERO)>0){
					refundRequest(wxcfgid, order.getOrderId(), refundFee, totalFee);//微信退款
				}
				saveOrderCancelStatus(order, operator, operatorType, reason); // 更新订单状态为：已取消
			} 
			
			//担保支付,退微信
			if(order.getPayType() == 2  && order.getPayStatus() == 1){
				int refundFee = order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue(); 
				int totalFee = refundFee;
				if(order.getTotalPrice().compareTo(BigDecimal.ZERO)>0){
					refundRequest(wxcfgid, order.getOrderId(), refundFee, totalFee);// 退款
				}
				saveOrderCancelStatus(order, operator, operatorType, reason);// 更新订单状态为：已取消
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("取消订单失败：" + e.getMessage());
		}
	}

	/**
	 * 微信退款请求
	 * 
	 * @param order
	 */
	@Transactional
	public void refundRequest(String wxcfgid, String orderId, int refundFee, int totalFee) {
		
		try {
			WxCfg wxcfg = wxCfgService.selectByKey(wxcfgid);
			if( null == wxcfg ) {
				throw new RuntimeException("wxcfg is null");
			}
			PaySetting set = new PaySetting();
			set.setAppId(wxcfg.getAppId());
			set.setMchId(wxcfg.getMchId());
			set.setKey(wxcfg.getAppkey());
			set.setCertPath(wxcfg.getApiclientCert());
//			set.setCertPath("F:\\wxzhengshu\\apiclient_cert.p12"); //本地测试
			set.setCertPassword(wxcfg.getMchId());

			Payments pay = Payments.with(set);
			RefundRequest req = new RefundRequest();
			req.setOperatorId(wxcfg.getMchId());
			// 需要“元”转化为“分”
			req.setRefundFee(refundFee);
			req.setRefundFeeType("CNY");// 币种（人民币）
			req.setRefundNumber(orderId);// 商户退款单号
			req.setTotalFee(totalFee);
			req.setTradeNumber(orderId);// 商户订单号(与下方微信订单号二选一)
			req.setTransactionId("");// 微信订单号

			RefundResponse resp = pay.refund(req);
			if (resp != null && "FAIL".equals(resp.getResultCode())) {
				String errorCode = resp.getErrorCode();
				String errorCodeDesc = resp.getErrorCodeDesc();
				throw new RuntimeException(errorCode + ":" + errorCodeDesc);
			}
			refundAccount(orderId);//记录微信退款
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(orderId+"微信退款对款失败"+e.getMessage());
		}
	}

	public void refundAccount(String orderId) {
		List<MemberAccountLog> AccountList = memberInfoService.getConsumingRecordByUser(null, null, orderId,"1");
		if(null != AccountList && AccountList.size()>0){
			for (MemberAccountLog account : AccountList) {
				account.setPayStatus("2");
				memberAccountLogService.updateNotNull(account);
			}
		}
	}

	/**
	 * 更新订单取消状态
	 * 
	 * @param order
	 * @param operator
	 * @param operatorType
	 */
	private void saveOrderCancelStatus(OrderInfo order, String operator, int operatorType, String reason) {
		order.setOrderStatus(HotelConstant.ORDER_STATUS_CANCELED);
		order.setCancelPerson(operator);
		order.setCancelTime(new Date());
		order.setCancelType(operatorType);
		order.setCancelReason(reason);
		updateAll(order);
	}

	@Override
	public Integer countOrders(String hotel, String orderStatus, String startTime, String endTime) {
		return orderInfoMapper.countOrders(hotel, orderStatus, startTime, endTime);
	}
	
	@Override
	public Integer countRepeatedPurchaseUsers(String hotelId, String orderStatus, Integer repeatCount) {
		return orderInfoMapper.countRepeatedPurchaseUsers(hotelId, orderStatus, repeatCount);
	}

	@Override
	public Integer countMemberTypeOrders(String hotelId, String memberId, String payStatus, String orderStatus, String startTime,
			String endTime) {
		return orderInfoMapper.countMemberTypeOrders(hotelId, memberId, payStatus, orderStatus, startTime, endTime);
	}
	
	@Override
	public Integer countMemberConfirmedOrders(String hotelId, String subject, String payStatus, String orderStatus,
			String startTime, String endTime) {
		return orderInfoMapper.countMemberConfirmedOrders(hotelId, subject, payStatus, orderStatus,
				startTime, endTime);
	}

	@Override
	public Integer countMemberOrders(String hotelId, String subject, String orderStatus, String startTime, String endTime) {
		return orderInfoMapper.countMemberOrders(hotelId, subject, orderStatus, startTime, endTime);
	}

	@Override
	public BigDecimal countOrdersIncome(String hotel, String startTime, String endTime) {
		return orderInfoMapper.countOrdersIncome(hotel, startTime, endTime);
	}
	

	@Override
	public Integer countEffectiveOrders(String hotel, String startTime, String endTime) {
		return orderInfoMapper.countEffectiveOrders(hotel, startTime, endTime);
	}

	@Override
	public BigDecimal countEffectiveOrdersIncome(String hotel, String startTime, String endTime) {
		return orderInfoMapper.countEffectiveOrdersIncome(hotel, startTime, endTime);
	}

	@Override
	public List<OrderInfo> findOrderList(OrderInfo orderInfo, int page, int rows) {
		try {
//			PageHelper.startPage(page, rows);
			PageHelper.startPage(page, rows,true,false,true); //设置无效页数不查询数据
			Example example = new Example(OrderInfo.class);
			Criteria createCriteria = example.createCriteria();
			if (orderInfo.getHotelId() != null) {
				createCriteria.andCondition("hotel_id=", orderInfo.getHotelId());
			}
			if (StringUtils.isNotBlank(orderInfo.getMemberId())) {
				createCriteria.andCondition("member_id=", orderInfo.getMemberId());
			}
			if (StringUtils.isNotBlank(orderInfo.getOrderStatus()) ) {
				if(!orderInfo.getOrderStatus().equals("13") ){
					createCriteria.andCondition("order_status=", orderInfo.getOrderStatus());
				}else{
					createCriteria.andCondition("order_status!=",2);
				}
			}
			if (orderInfo.getPayStatus() != null  && !orderInfo.getPayStatus().equals("123") ) {
				if(orderInfo.getPayStatus()==123){
					createCriteria.andCondition("pay_status != ",0);
				}else{
					createCriteria.andCondition("pay_status=", orderInfo.getPayStatus());
				}
			}
			example.setOrderByClause("order_sn DESC");
			return this.selectByExample(example);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 新订单 
	 */

	@Transactional
	@Override
	public Integer saveNewOrder(OrderInfo orderInfo) {
		orderInfo.setMoneyPaid(orderInfo.getUseBalance());  //默认已付金额0
		//  根据支付类型,设置支付状态
		if (orderInfo.getPayType() == 1) {  // 到店支付
			orderInfo.setPayStatus(2);// 到店支付
		} else if (orderInfo.getPayType() == 2) {// 担保支付
			orderInfo.setPayStatus(3);// 担保
		} else if (orderInfo.getPayType() == 3) {// 全额支付
			orderInfo.setPayStatus(0);// 未支付
		}
		orderInfo.setOrderId(Atools.getOneStr());// 订单id
		orderInfo.setBookTime(new Date());// 下单时间
		orderInfo.setOrderStatus(HotelConstant.ORDER_STATUS_NEW);// 订单状态：待确认
		//使用优惠券,则修改优惠券状态
		if(null !=orderInfo.getCouponId()){
			memberCouponService.updateStatus(orderInfo.getMemberId(),orderInfo.getHotelId(),orderInfo.getCouponId(),"1","1");
		}
		return this.orderInfoMapper.saveOrderInfo(orderInfo);
	}

	/**
	 * 创建统一订单
	 * 
	 * @param openid    openid
	 * @param orderType 订单类型（订房、充值）
	 * @param orderId   订单id
	 * @param productId 产品id
	 * @param createdIp 创建ip
	 * @param notifyUrl 通知地址
	 * @param fee       支付金额
	 * @return
	 */
	@Transactional(rollbackFor = Exception.class)
	public PaySignModel createUnifiedOrder(String wxcfgid, String openid, String createdIp, String orderId) {
		PaySignModel signModel = new PaySignModel();
		try{
			OrderInfo orderInfo = selectByKey(orderId);
			//订单不存在
			if (orderInfo == null) {
				throw new RuntimeException("invalid order id");
			}
			// 订单已支付
			if (orderInfo.getPayStatus() == 1) {
				throw new RuntimeException("订单已支付");
			}
			// 到店支付
			if (orderInfo.getPayType() == 1) {
				throw new RuntimeException("到店支付订单不需要支付");
			}
			//判断是否取消
			if ( "2".equals(orderInfo.getOrderStatus())){
				throw new RuntimeException("订单已取消");
			}
	
			// 使用了余额支付
			if (orderInfo.getMoneyPaid() != null && orderInfo.getMoneyPaid().compareTo(BigDecimal.ZERO) > 0) {
				MemberInfo memberInfo = memberInfoService.getMemberInfoForUpdate(orderInfo.getMemberId());
				BigDecimal balance = memberInfo.getRealMoney().add(memberInfo.getVirtMoney()); //账户总金额
				
				// 账户总余额  小于支付价格
				if (balance.compareTo(orderInfo.getMoneyPaid()) < 0) {
					throw new RuntimeException("余额不足");
				}
				if (memberInfo.getRealMoney().subtract(orderInfo.getMoneyPaid()).compareTo(BigDecimal.ZERO) >= 0) {// 实际账户 >= 已付金额
					// 新实际账户=原实际-花费余额 
					memberInfo.setRealMoney(memberInfo.getRealMoney().subtract(orderInfo.getMoneyPaid()));
					//记录本次消费
					if(orderInfo.getTotalPrice().compareTo(BigDecimal.ZERO) == 0){
						recordAccountLog(orderInfo, orderInfo.getMoneyPaid(), null, "1","1"); 
					}else{
						recordAccountLog(orderInfo, orderInfo.getMoneyPaid(), null, "1","0"); 
					}
				} else { // 实际账户 < 已付金额  (虚拟补差价)
					BigDecimal remain = orderInfo.getMoneyPaid().subtract(memberInfo.getRealMoney()); //应付 - 实际  = 差价
					memberInfo.setVirtMoney(memberInfo.getVirtMoney().subtract(remain));  //新虚拟账户 = 老虚拟-实际账户缺的差价
					memberInfo.setRealMoney(BigDecimal.ZERO);//新实际账户置0
					//记录本次消费   (真实 + 虚拟 )
					if(orderInfo.getTotalPrice().compareTo(BigDecimal.ZERO) == 0){
						recordAccountLog(orderInfo, orderInfo.getMoneyPaid().subtract(remain), remain, "2","1");
					}else{
						recordAccountLog(orderInfo, orderInfo.getMoneyPaid().subtract(remain), remain, "2","0");
					}
				}
				memberInfoService.updateAll(memberInfo);// 更新用户余额
			}
			
			if( ! StringUtils.isNotBlank(wxcfgid)){ //查询员工端wxcfgid 
				 WxCfg queryWxCfg = wxCfgService.queryWxCfg(orderInfo.getHotelId()+"", "1");
				 wxcfgid = (wxcfgid == null ? queryWxCfg.getWxcfgid() : null) ;
			}
			// 不需要微信支付
			if (orderInfo.getTotalPrice().compareTo(BigDecimal.ZERO) == 0) {  
				//到店支付
				if (orderInfo.getPayType() == 1){
					orderInfo.setPayStatus(2);//到店支付
				}
				// 担保支付
				if (orderInfo.getPayType() == 2) {
					orderInfo.setPayStatus(3);// 担保支付
				}
				// 全额支付
				if (orderInfo.getPayType() == 3) {
					orderInfo.setPayStatus(1);// 全额支付
				}
				updateAll(orderInfo);
				signModel.setAppId("no pay"); //不需要微信支付,特殊标识
				return signModel;
			}
			//调取微信
			signModel = pay4Weixin(wxcfgid, openid, createdIp, orderInfo);
			
			/*//调取wl微信支付
			Map<String, String> reqMap = new HashMap<>();
	        reqMap.put("out_trade_no", orderInfo.getOrderId());
	        reqMap.put("body","订房订单");
	        reqMap.put("total_fee", String.valueOf(orderInfo.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue()));
	        reqMap.put("service","weixin.mp");
	        reqMap.put("hotel_id", String.valueOf(orderInfo.getHotelId()));
	        reqMap.put("attach", "");
	        reqMap.put("create_ip", "127.0.0.1");
	        reqMap.put("openid", openid);
	        reqMap.put("notifyUrl", miniprogramServerUrl+"/rest/orders/" + wxcfgid + "/"
					+ orderInfo.getOrderId() + "/payNotify");
	 
			String res = HttpRequestUtil.send(payUrl+"pay", reqMap, "utf-8");
			logger.info(orderId+"号订单调取支付接口返回参数为:"+res);
			if(null == res ){
				logger.info("调取支付接口失败,订单为id:"+orderId);
				throw new RuntimeException("调取支付接口失败");
			}
			JSONObject jsonObject = JSONObject.fromObject(res); 
			if(!("SUCCESS").equals(jsonObject.get("return_code"))){
				logger.info("调取支付接口失败,订单为id:"+orderId);
				throw new RuntimeException("调取支付接口失败");
			}
			String prepayId = (String) jsonObject.get("prepay_id");
			String appid = (String) jsonObject.get("appid");
			String nonceStr = (String) jsonObject.get("nonce_str");
			String packageNO = (String) jsonObject.get("package");
			String signType = (String) jsonObject.get("signType");
			String timeStamp = (String) jsonObject.get("timeStamp");
			String sign = (String) jsonObject.get("sign");
			// 更新prepayid
			orderInfo.setPrepayId(prepayId);
			updateAll(orderInfo);
	
			signModel.setAppId(appid);
			signModel.setTimeStamp(timeStamp);
			signModel.setNonceStr(nonceStr);
			signModel.setPackage("prepay_id="+packageNO);
			signModel.setSignType(signType);
			signModel.setPaySign(sign);*/
		}catch(Exception e){
			e.printStackTrace();
			logger.info("微信支付失败,订单号:"+orderId,e.getMessage());
		}
		return signModel;
	}

	/**
	 * 微信支付
	 * @param wxcfgid
	 * @param openid
	 * @param createdIp
	 * @param orderInfo
	 * @return
	 */
	private PaySignModel pay4Weixin(String wxcfgid, String openid, String createdIp, OrderInfo orderInfo) {
		
			// 微信支付通知
			String notifyUrl = miniprogramServerUrl+"/rest/orders/" + wxcfgid + "/"
					+ orderInfo.getOrderId() + "/payNotify";
			
			WxCfg wxCfg = wxCfgService.selectByKey(wxcfgid);
			if(wxCfg == null) return null;
			
			PaySignModel signModel = new PaySignModel();
			PaySetting set = new PaySetting();
			set.setAppId(wxCfg.getAppId());
			set.setMchId(wxCfg.getMchId());
			set.setKey(wxCfg.getAppkey());
			set.setCertPath(wxCfg.getApiclientCert());
	//		set.setCertPath("F:\\wxzhengshu\\apiclient_cert.p12");
			set.setCertPassword(wxCfg.getMchId());
	
			Payments pay = Payments.with(set);
			UnifiedOrderRequest req = new UnifiedOrderRequest();
			req.setBody("订房订单");
			req.setTradeNumber(orderInfo.getOrderId());
			req.setTotalFee(orderInfo.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue());// 分转为元
			req.setBillCreatedIp(createdIp);
			req.setNotifyUrl(notifyUrl);
			req.setTradeType("JSAPI");// 小程序类型取值
			req.setProductId(String.valueOf(orderInfo.getRoomId()));
			req.setOpenId(openid);
			UnifiedOrderResponse resp = pay.unifiedOrder(req);
			
		System.out.println("createUnifiedOrder-----openid=" + openid + ",orderid=" + orderInfo.getOrderId());
		logger.debug("createUnifiedOrder-----openid=" + openid + ",orderid=" + orderInfo.getOrderId());

		// 失败抛出异常
		if (resp.getResultCode().equals("FAIL")) {
			throw new RuntimeException("FAIL:" + resp.getReturnMessage());
		}
		// 更新prepayid
		orderInfo.setPrepayId(resp.getPrepayId());
		updateAll(orderInfo);

		SortedMap<String, Object> generals = new TreeMap<>();
		generals.put("appId", wxCfg.getAppId());
		generals.put("nonceStr", Atools.getOneKeyS());
		generals.put("package", "prepay_id=" + resp.getPrepayId());
		generals.put("signType", "MD5");
		generals.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));

		String sign = SignatureUtil.sign(generals, wxCfg.getAppkey());

		signModel.setAppId((String) generals.get("appId"));
		signModel.setTimeStamp((String) generals.get("timeStamp"));
		signModel.setNonceStr((String) generals.get("nonceStr"));
		signModel.setPackage((String) generals.get("package"));
		signModel.setSignType("MD5");
		signModel.setPaySign(sign);
		
		recordAccountLog(orderInfo, orderInfo.getTotalPrice(),null ,"3","0"); //记录用户消费记录
		return signModel;
	}

	/**
	 * 该方法用于记录用户消费记录
	 * 
	 * @param orderInfo
	 * @param moneyPaid 实际账户 
	 * @param virtMoney 虚拟账户 
	 * @param type 消费类型 
	 * @param payStatus 
	 */
	public void recordAccountLog(OrderInfo orderInfo,BigDecimal realMoney,BigDecimal virtMoney,String type, String payStatus) {
		MemberAccountLog accountLog = new MemberAccountLog();
		accountLog.setOrderId(orderInfo.getOrderId());
		accountLog.setHotelId(orderInfo.getHotelId()+"");
		accountLog.setMemberId(orderInfo.getMemberId());
		accountLog.setPayMode("1"); // 1:订单    2:充值  
		accountLog.setPayTime(df2.format(new Date()));
		accountLog.setCouponId(orderInfo.getCouponId()+"");
		accountLog.setRealMoney(realMoney);
		accountLog.setVirtMoney( virtMoney == null ? new BigDecimal("0"):virtMoney );
		accountLog.setType(type);
		accountLog.setPayStatus(payStatus);
		accountLog.setAddTime(df2.format(new Date()));
		memberInfoService.insertConsumingRecord(accountLog); //写入用户消费记录
	}

	/**
	 * 根据条件查询所有订单
	 */
	@Override
	public ArrayList<OrderInfo> findOrderListByQuery(OrderInfo orderInfo, int page, int rows) {
		QueryVo2OrderInfo queryVo2OrderInfo = new QueryVo2OrderInfo();
		queryVo2OrderInfo.setOrderInfo(orderInfo);
		return orderInfoMapper.findOrderList(queryVo2OrderInfo);
	}

	@Override
	public List<OrderInfo> findUserOrderListByMoreState(String memberId, String orderStatus, String payStatus, int page,
			int rows) {
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setMemberId(memberId);
		orderInfo.setOrderStatus(orderStatus);
		orderInfo.setPayStatus(Integer.parseInt(payStatus));
		return this.findOrderList(orderInfo, page, rows);
	}

	@Override
	public List<OrderInfo> findHotelOrderListByMoreState(String hotelId, String orderStatus, String payStatus, int page,
			int rows) {
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setHotelId(Integer.valueOf(hotelId));
		orderInfo.setOrderStatus(orderStatus);
		orderInfo.setPayStatus(Integer.parseInt(payStatus));
		return this.findOrderList(orderInfo, page, rows);
	}

	@Override
	public List<OrderInfo> findUserOrderListByUnCheckIn(String memberId, int page, int rows) {
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setMemberId(memberId);
		/*Date date = new Date();
		String checkinTime = df.format(date);
		orderInfo.setCheckinTime(checkinTime);*/
		return orderInfoMapper.findUserOrderListByUnCheckIn(orderInfo);
	}

	@Override
	public List<OrderInfo> findHotelOrderListByUnCheckIn(String hotelId, int page, int rows) {
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setHotelId(Integer.parseInt(hotelId));
		Date date = new Date();
		String checkinTime = df.format(date);
		orderInfo.setCheckinTime(checkinTime);
		return orderInfoMapper.findUserOrderListByUnCheckIn(orderInfo);
	}

	@Override
	public void cancelOrderByAutoTask(String beginTime) {
		orderInfoMapper.cancelOrderByAutoTask(beginTime);
	}

	//统计待确认订单
	@Override
	public Integer countWaitOrder(String hoteld, String orderStatus, String payStatus) {
		
		return orderInfoMapper.countWaitOrder(hoteld,orderStatus, payStatus);
	}
	@Override
	public List<OrderInfo> findCancleOrderListSql(String hotelId, String orderStatus, int page, int rows) {
		PageHelper.startPage(page, rows,true,false,true);//设置无效页数不查询数据
		return this.orderInfoMapper.findCancleOrderListSql(hotelId,orderStatus,page,rows);
	}
	
	@Override
	public List<OrderInfo> findConfirmOrderListSql(String hotelId, String orderStatus, int page, int rows) {
		PageHelper.startPage(page, rows,true,false,true); //设置无效页数不查询数据
		return this.orderInfoMapper.findConfirmOrderListSql(hotelId,orderStatus,page,rows);
	}

	@Override
	public Integer updateByOrderStatusSelective(String orderStatus,String startTime,String endTime, String oldStatus) {
		return orderInfoMapper.updateByOrderStatusSelective(orderStatus,startTime,endTime,oldStatus);
	}
	
	@Override
	public Integer updateByOrderStatusOver(String orderStatus, String startTime, String endTime, String oldStatus) {
		return orderInfoMapper.updateByOrderStatusOver(orderStatus,startTime,endTime,oldStatus);
	}
	
	@Override
	public Integer updateByOrderStatusOverByHotelId(String orderStatus, String startTime, String endTime, String oldStatus, String hotelId) {
		return orderInfoMapper.updateByOrderStatusOverByHotelId(orderStatus,startTime,endTime,oldStatus,hotelId);
	}

	@Override
	public OrderInfo getOrderInfo(OrderInfo orderInfo) {
		return orderInfoMapper.selectByPrimaryKey(orderInfo.getOrderId());
	}

	@Override
	public Integer updateByOrderSnSelective(OrderInfo orderInfo) {
		return orderInfoMapper.updateByOrderSnSelective(orderInfo);
	}

	@Override
	public void sendSms(String messageType, OrderInfo orderInfo) {
		
		HotelInfo hotel = hotelInfoService.selectByKey(String.valueOf(orderInfo.getHotelId()));
		if(hotel == null) {
			throw new RuntimeException( " invalid hotel id :" + orderInfo.getHotelId());
		}
		
		String msg = "";
		//新建：非到店、未支付
		if("0".equals(messageType) && orderInfo.getPayType() > 1 && orderInfo.getPayStatus() < 1) {
			msg = "订单创建成功，订单号为：" + orderInfo.getOrderSn() + "，30分钟未支付订单自动取消！";
		}
		//新建：到店支付
		if("0".equals(messageType) && orderInfo.getPayType() == 1){
			msg = "订单创建成功，等待酒店确认，订单号为：" + orderInfo.getOrderSn() + "。";
		}
		//已确认
		if("1".equals(messageType)) {
			msg = "您的订单已确认，订单号为" + orderInfo.getOrderSn();
		}
		//已取消
		if("2".equals(messageType)) {
			msg = "您的订单已取消，订单号为" + orderInfo.getOrderSn();
		}
		//支付成功
		if("3".equals(messageType)) {
			msg = "您的订单支付成功，订单号为" + orderInfo.getOrderSn();
		}
		
		simpleSmsUtil.sendMsg(orderInfo.getMobile(), hotel.getHotelName(), msg);
		
	}

	@Override
	public void sendSmsByOrderId(String messageType, String orderId) {
		OrderInfo order = selectByKey(orderId);
		if(order == null) {
			throw new RuntimeException("invalid order id : " + orderId);
		}
		sendSms(messageType, order);
	}

	@Override
	public List<OrderInfo> findHotelOrderAll(String hoteId, String bookTime) {
		Example example = new Example(OrderInfo.class);
		Criteria createCriteria = example.createCriteria();
		createCriteria.andCondition("hotel_id=", hoteId);
		createCriteria.andCondition("book_time<=", bookTime);
		createCriteria.andCondition("order_status!=", 0);
		return orderInfoMapper.selectByExample(example);
	}

	@Override
	public List<OrderInfo> findHotelOrderAll(String hotelId, int page, int rows) {
		PageHelper.startPage(page, rows,true,false,true); //设置无效页数不查询数据
		return orderInfoMapper.findHotelOrderAll(hotelId);
	}

	@Override
	public List<OrderInfo> findOrderToDay(String oldStatus, String startTime, String endTime) {
		return orderInfoMapper.findOrderToDay(oldStatus, startTime, endTime);
	}

	@Override
	public List<OrderInfo> findOrderToDayByHotelId(String oldStatus, String startTime, String endTime, String hotelId) {
		return orderInfoMapper.findOrderToDayByHotelId(oldStatus, startTime, endTime, hotelId);
	}

	@Override
	public List<OrderInfo> select30TimeoutOrder(String beginTime) {
		return orderInfoMapper.select30TimeoutOrder(beginTime);
	}

	@Override
	public List<OrderInfo> select12hTimeoutOrder(String beginTime) {
		return orderInfoMapper.select12hTimeoutOrder(beginTime);
	}
	
	@Override
	public List<OrderInfo> select12hTimeoutOrderByHotelId(String beginTime, String hotelId) {
		return orderInfoMapper.select12hTimeoutOrderByHotelId(beginTime, hotelId);
	}

	@Override
	public BigDecimal countMemberOrdersIncome(String memberId, String hotelId) {
		return orderInfoMapper.countMemberOrdersIncome(memberId, hotelId);
	}
}
